/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Graphics Module                                */
/*                                                                           */
/*    Copyright (C) 2005 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------   */
/*    guiFrameBuffer - Low level frame buffer routines                       */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <dosKernel.h>
#include <krnlResourceFile.h>
#include <guiBWGraphicsDriver.h>
#include <guiBWGraphics.h>
#include <guiCommon.h>
#include "sysCompiler.h"
#include "sysSettings.H"

///////////////////////////////////////////////////////////////////////////////
// Const

///////////////////////////////////////////////////////////////////////////////
// GUI frame buffer
#ifndef guiCUSTOM_FRAMEBUFFER
dosByte g_gui_frame_buffer[guiFRAME_BUFFER_ROW_LENGTH * guiSCREEN_HEIGHT];
#endif

///////////////////////////////////////////////////////////////////////////////
// Local variables
static dosByte l_pen_index;
static dosByte l_draw_mode;
static guiRect l_clip_rect;
static guiRect l_refresh_rect;
static dosByte l_custom_brush[guiCUSTOM_BRUSH_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

static dosROM_DATA( const dosByte, l_pixel_mask[8]) = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
static dosROM_DATA( const dosByte, l_pixel_row_start_mask[8]) = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
static dosROM_DATA( const dosByte, l_pixel_row_end_mask[8]) = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };

// system brushes
static dosROM_DATA( dosByte, l_system_brush[] ) = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		// solid black
													0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55,		// halftone
													0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff   // solid white brush
												};

// resource chunk addresses
//static dosROMAddress l_bitmap_resource_address;
//static dosROMAddress l_font_resource_address;

///////////////////////////////////////////////////////////////////////////////
// Functions
void guiBitBltFromROM(	dosROMAddress in_address,
												dosInt16 in_dest_x, dosInt16 in_dest_y,
												dosInt16 in_source_x, dosInt16 in_source_y, 
												dosInt16 in_width, dosInt16 in_height,
												dosInt16 in_bits_per_pixel );


///////////////////////////////////////////////////////////////////////////////
// Initialize GUI subsystem
void guiInitBWGraphics(void)
{
	// init driver
	guiInitBwGraphicsDriver();

	// init cliping rectangle
	l_clip_rect.Left	= 0;
	l_clip_rect.Top		= 0;
	l_clip_rect.Right		= guiSCREEN_WIDTH-1;
	l_clip_rect.Bottom	= guiSCREEN_HEIGHT-1;

  // init refresh rect
  l_refresh_rect.Left   = guiINVALID_COORDINATE;
  l_refresh_rect.Top    = guiINVALID_COORDINATE;
  l_refresh_rect.Right  = guiINVALID_COORDINATE;
  l_refresh_rect.Bottom = guiINVALID_COORDINATE;

	// init pen
	l_pen_index					= 1;
	l_draw_mode					= 0;

	// clear frame buffer
	guiClearScreen();

  // init resource addresses
  //l_bitmap_resource_address = krnlGetResourceAddress( 0x53504D42ul );
  //l_font_resource_address = krnlGetResourceAddress( 0x544E4F46ul );

	// init color conversion table

}

///////////////////////////////////////////////////////////////////////////////
// Clear screen
void guiClearScreen( void )
{
	int i;

	for( i = 0; i < guiFRAME_BUFFER_ROW_LENGTH * guiSCREEN_HEIGHT; i++ )
		*(g_gui_frame_buffer +i) = 0; 
}

///////////////////////////////////////////////////////////////////////////////
// Sets Drawing mode
void guiSetDrawMode( dosByte in_draw_mode )
{
	l_draw_mode = in_draw_mode;
}

///////////////////////////////////////////////////////////////////////////////
// Open canvas for drawing
void guiOpenCanvas( dosInt16 in_left, dosInt16 in_top, dosInt16 in_right, dosInt16 in_bottom )
{
	dosInt16 i;

	// swap coordinates

	// x
	if( in_right < in_left )
	{
		i = in_right;
		in_right = in_left;
		in_left = i;
	}

	// y
	if( in_bottom < in_top )
	{
		i = in_bottom;
		in_bottom = in_top;
		in_top = i;
	}

	// check left coordinate
	if( in_left < 0 )
		in_left = 0;

	if( in_left >= guiSCREEN_WIDTH )
		in_left = guiSCREEN_WIDTH - 1;

	// check right coordinate
	if( in_right < 0 )
		in_right = 0;

	if( in_right >= guiSCREEN_WIDTH )
		in_right = guiSCREEN_WIDTH - 1;

	// check top coordinate
	if( in_top < 0 )
		in_top = 0;

	if( in_top >= guiSCREEN_HEIGHT )
		in_top = guiSCREEN_HEIGHT - 1;

	// check bottom coordinate
	if( in_bottom < 0 )
		in_bottom = 0;

	if( in_bottom >= guiSCREEN_HEIGHT )
		in_bottom = guiSCREEN_HEIGHT - 1;

	// store coordinates
	l_clip_rect.Left		= in_left;
	l_clip_rect.Top			=	in_top;
	l_clip_rect.Right		= in_right;
	l_clip_rect.Bottom	= in_bottom;

  // expand refresh rectangle
  guiExtendRect( &l_refresh_rect, &l_clip_rect );
}

///////////////////////////////////////////////////////////////////////////////
// Close canvas
void guiCloseCanvas( dosBool in_refresh  )
{
  if( in_refresh )
    guiRefreshCanvas();
}

///////////////////////////////////////////////////////////////////////////////
// Update canvas
void guiRefreshCanvas( void )
{
  if( l_refresh_rect.Left != guiINVALID_COORDINATE &&
      l_refresh_rect.Top != guiINVALID_COORDINATE &&
      l_refresh_rect.Right != guiINVALID_COORDINATE &&
      l_refresh_rect.Bottom != guiINVALID_COORDINATE )
  {
	  guiUpdateCanvas( l_refresh_rect.Left, l_refresh_rect.Top, l_refresh_rect.Right, l_refresh_rect.Bottom );
  }
}

///////////////////////////////////////////////////////////////////////////////
// Set pen index
void guiSetPen( dosByte in_index )
{
	l_pen_index = in_index;
}



///////////////////////////////////////////////////////////////////////////////
// Set system brush
void guiSetSystemBrush( dosByte in_index )
{
	dosByte i;

	// check index
	if( in_index > dosGUISB_Last )
		return;

	// copy brush data
	for( i = 0; i < guiCUSTOM_BRUSH_SIZE; i++ )
	{
		l_custom_brush[i] = l_system_brush[in_index*guiCUSTOM_BRUSH_SIZE+i];
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw pixel
void guiDrawPixel( dosInt16 in_x, dosInt16 in_y )
{
	dosInt16 address;

	if( in_x < l_clip_rect.Left || in_x > l_clip_rect.Right ||
			in_y < l_clip_rect.Top || in_y > l_clip_rect.Bottom )
		return;

	address = in_y * guiFRAME_BUFFER_ROW_LENGTH + in_x / guiBITS_PER_PIXEL;

	switch( l_pen_index )
	{
		case 0:	
			g_gui_frame_buffer[address] &= ~(l_pixel_mask[in_x % guiBITS_PER_PIXEL]);
			break;

		case 1:
			g_gui_frame_buffer[address] |= l_pixel_mask[in_x % guiBITS_PER_PIXEL];
			break;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw rectangle
void guiDrawRectangle( dosInt16 in_left, dosInt16 in_top, dosInt16 in_right, dosInt16 in_bottom )
{
	guiDrawLine( in_left, in_top, in_right, in_top );
	guiDrawLine( in_right, in_top, in_right, in_bottom );
	guiDrawLine( in_left, in_bottom, in_right, in_bottom );
	guiDrawLine( in_left, in_top, in_left, in_bottom );
}

///////////////////////////////////////////////////////////////////////////////
// Fill rectangle
void guiFillRectangle( dosInt16 in_left, dosInt16 in_top, dosInt16 in_right, dosInt16 in_bottom )
{
  dosInt16 y;
	dosInt16 i;
	dosInt16 address1;
	dosInt16 address2;
	dosByte mask1;
	dosByte mask2;

	// exhange coordinates
	if( in_left > in_right )
	{
		i = in_left;
		in_left = in_right;
		in_right = i;
	}

	if( in_top > in_bottom )
	{
		i = in_top;
		in_top = in_bottom;
		in_bottom = (dosByte)i;
	}

  // do clipping
  if( in_left < l_clip_rect.Left )
    in_left = l_clip_rect.Left;

  if( in_right > l_clip_rect.Right )
    in_right = l_clip_rect.Right;

  if( in_top < l_clip_rect.Top )
    in_top = l_clip_rect.Top;

  if( in_bottom > l_clip_rect.Bottom )
    in_bottom = l_clip_rect.Bottom;

  if( in_right < l_clip_rect.Left || in_left > l_clip_rect.Right ||
      in_top > l_clip_rect.Bottom || in_bottom < l_clip_rect.Top ||
      in_right < in_left || in_bottom < in_top )
    return;

  // calculate addresses and masks
	// draw the line
	address1	=	address2 = in_top * guiFRAME_BUFFER_ROW_LENGTH;
	address1	+= in_left / guiBITS_PER_PIXEL;
	mask1			= l_pixel_row_start_mask[in_left % guiBITS_PER_PIXEL];
	address2	+= in_right /	guiBITS_PER_PIXEL;
	mask2			= l_pixel_row_end_mask[in_right % guiBITS_PER_PIXEL];
	if( address1 == address2 )
	{
    mask1 = mask1 & mask2;
		mask2 = ~mask1;

	  for( y = in_top; y <= in_bottom; y++ )
    {
			g_gui_frame_buffer[address1] = (l_custom_brush[y%guiCUSTOM_BRUSH_SIZE] & mask1) | (g_gui_frame_buffer[address1] & mask2);

      address1 += guiFRAME_BUFFER_ROW_LENGTH;
		}
	}
	else
	{
    for( y = in_top; y <= in_bottom; y++ )
    {
			g_gui_frame_buffer[address1] = (l_custom_brush[y%guiCUSTOM_BRUSH_SIZE] & mask1) | (g_gui_frame_buffer[address1] & ~mask1) ;

			for( i = address1 + 1; i < address2; i++ )
				g_gui_frame_buffer[i] = l_custom_brush[y%guiCUSTOM_BRUSH_SIZE];

			g_gui_frame_buffer[address2] = (l_custom_brush[y%guiCUSTOM_BRUSH_SIZE] & mask2) | (g_gui_frame_buffer[address2] & ~mask2) ;

      address1 += guiFRAME_BUFFER_ROW_LENGTH;
      address2 += guiFRAME_BUFFER_ROW_LENGTH;
    }
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw horizontal line
void guiDrawHorizontalLine( dosInt16 in_startx, dosInt16 in_endx, dosInt16 in_y )
{
	dosInt16 i;
	dosInt16 address1;
	dosInt16 address2;
	dosByte mask1;
	dosByte mask2;

  // don't draw if the line is not visible
	if( in_y < l_clip_rect.Top || in_y > l_clip_rect.Bottom )
		return;

	// exchange coorinates if necessary
	if( in_startx > in_endx )
	{
		i = in_startx;
		in_startx = in_endx;
		in_endx = i;		
	}

	// do clipping
	if( in_startx < l_clip_rect.Left )
		in_startx = l_clip_rect.Left;

	// right side
	if( in_endx > l_clip_rect.Right )
		in_endx = l_clip_rect.Right;

	// if outside clipping rectangle
	if( in_endx < in_startx )
		return;

	// if only one pixel requested
	if( in_startx == in_endx )
		guiDrawPixel( in_startx, in_y );

	// draw the line
	address1	=	address2 = in_y * guiFRAME_BUFFER_ROW_LENGTH;
	address1	+= in_startx / guiBITS_PER_PIXEL;
	mask1			= l_pixel_row_start_mask[in_startx % guiBITS_PER_PIXEL];
	address2	+= in_endx /	guiBITS_PER_PIXEL;
	mask2			= l_pixel_row_end_mask[in_endx % guiBITS_PER_PIXEL];
	if( address1 == address2 )
	{
		switch( l_pen_index )
		{
			// backgound pen
			case 0:
				g_gui_frame_buffer[address1] &= ~(mask1 & mask2);
				break;

			// foreground pen
			case 1:
				g_gui_frame_buffer[address1] |= mask1 & mask2;
				break;
		}
	}
	else
	{
		switch( l_pen_index )
		{
			// background pen
			case 0:
				g_gui_frame_buffer[address1] &= ~mask1;
	
				for( i = address1 + 1; i < address2; i++ )
					g_gui_frame_buffer[i] = 0;

				g_gui_frame_buffer[address2] &= ~mask2;

				break;

			// foreground pen
			case 1:
				g_gui_frame_buffer[address1] |= mask1;
	
				for( i = address1 + 1; i < address2; i++ )
					g_gui_frame_buffer[i] = 0xff;

				g_gui_frame_buffer[address2] |= mask2;

				break;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw vertical lines
void guiDrawVerticalLine( dosInt16 in_x, dosInt16 in_starty, dosInt16 in_endy )
{
	dosInt16 i;
	dosInt16 address1;
	dosByte mask1;

  // don't draw if line is hidden
  if( in_x < l_clip_rect.Left || in_x > l_clip_rect.Right )
	  return;

  // exchange coorinates if necessary
  if( in_starty > in_endy )
  {
	  i = in_starty;
	  in_starty = in_endy;
	  in_endy = i;		
  }

  // top side
  if( in_starty < l_clip_rect.Top )
	  in_starty = l_clip_rect.Top;

  // bottom side
  if( in_endy > l_clip_rect.Bottom )
	  in_endy = l_clip_rect.Bottom;

	// if outside clipping rectangle
	if( in_endy < in_starty )
		return;

	// draw the line
  address1	=	in_starty * guiFRAME_BUFFER_ROW_LENGTH + in_x / guiBITS_PER_PIXEL;
  mask1			= l_pixel_mask[in_x % guiBITS_PER_PIXEL];

  switch( l_pen_index )
  {
	  // backgound pen
	  case 0:
		  mask1 = ~mask1;
		  while( in_starty <= in_endy )
		  {
			  g_gui_frame_buffer[address1] &= mask1;
			  address1 += guiFRAME_BUFFER_ROW_LENGTH;
			  in_starty++;
		  }
		  break;

	  // foreground pen
	  case 1:
		  while( in_starty <= in_endy )
		  {
			  g_gui_frame_buffer[address1] |= mask1;
			  address1 += guiFRAME_BUFFER_ROW_LENGTH;
			  in_starty++;
		  }
		  break;
  }
}

///////////////////////////////////////////////////////////////////////////////
// Draw line
void guiDrawLine( dosInt16 in_startx, dosInt16 in_starty, dosInt16 in_endx, dosInt16 in_endy )
{
	dosInt16 i;
	dosInt16 address1;
	dosInt16 address2;
	dosByte mask1;
	dosInt16 dx, dy;
	dosInt16 dx_abs, dy_abs;

	/**************************/
	/* handle horizontal line */
	/**************************/
	if( in_starty == in_endy )
	{
    guiDrawHorizontalLine( in_startx, in_endx, in_starty );
	}
	else
	{
		/*************************/
		/* handle vertical lines */
		/*************************/
		if( in_startx == in_endx )
		{
      guiDrawVerticalLine( in_startx, in_starty, in_endy );
		}
		else
		{
			/*************************/
			/* handle any other line */
			/*************************/
			dx = in_endx - in_startx;
			dy = in_endy - in_starty;

			///////////////////////
			// do cliping 
			///////////////////////
			// 'start' coordinates clipping
			//
			// (startx <-> left side)
			if( in_startx < l_clip_rect.Left )
			{
				// if the line is outside
				if( in_endx < l_clip_rect.Left )
					return;
				
				// try to find clip point
				i = l_clip_rect.Left - in_startx;

				dy_abs = in_starty + (dosInt16)(( ((dosInt32)i) * 256 * dy / dx ) / 256);

				if( dy_abs >= l_clip_rect.Top && dy_abs <= l_clip_rect.Bottom )
				{
					in_startx = l_clip_rect.Left;
					in_starty = dy_abs;
				}
			}
			else
			{
				// startx <-> right side
				if( in_startx > l_clip_rect.Right )
				{
					// if the line is outside
					if( in_endx > l_clip_rect.Right )
						return;

					// try to find clip ponit
					i = l_clip_rect.Right - in_startx;

					dy_abs = in_starty + (dosInt16)((  ((dosInt32)i) * 256 * dy / dx ) / 256);
	
					if( dy_abs >= l_clip_rect.Top && dy_abs <= l_clip_rect.Bottom )
					{
						in_startx = l_clip_rect.Right;
						in_starty = dy_abs;
					}
				}
			}

			// do clipping (starty <-> top side)
			if( in_starty < l_clip_rect.Top )
			{
				// if the line is outside
				if( in_endy < l_clip_rect.Top )
					return;
				
				// try to find clip point
				i = l_clip_rect.Top - in_starty;

				dx_abs = in_startx + (dosInt16)(( ((dosInt32)i) * 256 * dx / dy ) / 256);

				if( dx_abs >= l_clip_rect.Left && dx_abs <= l_clip_rect.Right )
				{
					in_startx = dx_abs;
					in_starty = l_clip_rect.Top;
				}
			}
			else
			{
				// starty <-> bottom side
				if( in_starty > l_clip_rect.Bottom )
				{
					// if the line is outside
					if( in_endy > l_clip_rect.Bottom )
						return;

					// try to find clip ponit
					i = l_clip_rect.Bottom - in_starty;

					dx_abs = in_startx + (dosInt16)(( ((dosInt32)i) * 256 * dx / dy ) / 256);
		
					if( dx_abs >= l_clip_rect.Left && dx_abs <= l_clip_rect.Right )
					{
						in_startx = dx_abs;
						in_starty = l_clip_rect.Bottom;
					}
				}
			}

			///////////////////////
			// 'end' coordinates clipping
			//
			// (endx<->left side)
			if( in_endx < l_clip_rect.Left )
			{
				// try to find clip point
				i = in_endx - l_clip_rect.Left;

				dy_abs = in_endy - (dosInt16)(( ((dosInt32)i) * 256 * dy / dx ) / 256);

				if( dy_abs >= l_clip_rect.Top && dy_abs <= l_clip_rect.Bottom )
				{
					in_endx = l_clip_rect.Left;
					in_endy = dy_abs;
				}
			}
			else
			{
				// endx <-> right side
				if( in_endx > l_clip_rect.Right )
				{
					// try to find clip ponit
					i = in_endx - l_clip_rect.Right;

					dy_abs = in_endy - (dosInt16)(( ((dosInt32)i) * 256 * dy / dx ) / 256);

					if( dy_abs >= l_clip_rect.Top && dy_abs <= l_clip_rect.Bottom )
					{
						in_endx = l_clip_rect.Right;
						in_endy = dy_abs;
					}
				}
			}

			// do clipping (endy <-> top side)
			if( in_endy < l_clip_rect.Top )
			{
				// try to find clip point
				i = in_endy - l_clip_rect.Top;

				dx_abs = in_endx - (dosInt16)(( ((dosInt32)i) * 256 * dx / dy ) / 256);

				if( dx_abs >= l_clip_rect.Left && dx_abs <= l_clip_rect.Right )
				{
					in_endx = dx_abs;
					in_endy = l_clip_rect.Top;
				}
			}
			else
			{
				// starty <-> bottom side
				if( in_endy > l_clip_rect.Bottom )
				{
					// try to find clip ponit
					i =  in_endy - l_clip_rect.Bottom;

					dx_abs = in_endx - (dosInt16)(( ((dosInt32)i) * 256 * dx / dy ) / 256);

					if( dx_abs >= l_clip_rect.Left && dx_abs <= l_clip_rect.Right )
					{
						in_endx = dx_abs;
						in_endy = l_clip_rect.Bottom;
					}
				}
			}

			// check again, it it sill outside, their is nothing to draw
			if( in_startx < l_clip_rect.Left || in_startx > l_clip_rect.Right ||
					in_starty < l_clip_rect.Top || in_starty > l_clip_rect.Bottom ||
					in_endx < l_clip_rect.Left || in_endx > l_clip_rect.Right ||
					in_endy < l_clip_rect.Top || in_endy > l_clip_rect.Bottom )
				return;

			// update slope
			dx = in_endx - in_startx;
			dy = in_endy - in_starty;

			// if it's only one pixel
			if( dx == 0 && dy == 0 )
			{
				guiDrawPixel( in_startx, in_starty );
				return;
			}

			if( dx < 0 )
				dx_abs = -dx;
			else
				dx_abs = dx;

			if( dy < 0 )
				dy_abs = -dy;
			else
				dy_abs = dy;

			// decide main axis
			if( dy_abs > dx_abs )
			{
				//////////////////
				// go along Y axis

				// get mask index (i), mask (mask1) and pixel address (address1)
				i = in_startx % guiBITS_PER_PIXEL;
				if( l_pen_index == 0 )
					mask1 = ~l_pixel_mask[i];
				else
					mask1	= l_pixel_mask[i];

				address1 = in_starty * guiFRAME_BUFFER_ROW_LENGTH + in_startx/guiBITS_PER_PIXEL;

				// initializie fractional part (address2)
				address2 = dy_abs/2;

				// draw the line
				while( 1 ) // for efficiency leave the loop using break
				{
					// do selected operation (or, and)
					if( l_pen_index == 0 )
						g_gui_frame_buffer[address1] &= mask1;
					else
						g_gui_frame_buffer[address1] |= mask1;

					// calculate fractional part
					address2 += dx_abs;

					// overflow of fractional part -> do X step
					if( address2 >= dy_abs )
					{
						address2 -= dy_abs;// remove integer part

						// get new address and mask index
						if( dx < 0 )
						{
							if( i == 0 )
							{
								i = 7;
								address1--;
							}
							else
								i--;
						}
						else
						{
							if( i == 7 )
							{
								i = 0;
								address1++;
							}
							else
								i++;
						}

						// get new mask
						if( l_pen_index == 0 )
							mask1 = ~l_pixel_mask[i];
						else
							mask1	= l_pixel_mask[i];
					}

					// exit from the loop
					if( in_starty == in_endy )
						break;

					// next line
					if( dy > 0 )
					{
						in_starty++;
						address1 += guiFRAME_BUFFER_ROW_LENGTH; 
					}
					else
					{
						in_starty--;
						address1 -= guiFRAME_BUFFER_ROW_LENGTH; 
					}
				}
			}
			else
			{
				//////////////////
				// go along X axis

				// get mask index (i), mask (mask1) and pixel address (address1)
				i = in_startx % guiBITS_PER_PIXEL;
				mask1	= l_pixel_mask[i];
				address1 = in_starty * guiFRAME_BUFFER_ROW_LENGTH + in_startx/guiBITS_PER_PIXEL;

				// initializie fractional part (address2)
				address2 = dx_abs / 2;

				// finally draw the line
				while( 1 ) // for efficiency leave the loop using break
				{
					address2 += dy_abs;

					if( address2 >= dx_abs )
					{
						address2 -= dx_abs; // remove integer part

						// put pixels
						if( l_pen_index == 0 )
							g_gui_frame_buffer[address1] &= ~mask1;
						else
							g_gui_frame_buffer[address1] |= mask1;

						mask1 = 0;

						// new address
						if( dy < 0 )
							address1 -= guiFRAME_BUFFER_ROW_LENGTH;
						else
							address1 += guiFRAME_BUFFER_ROW_LENGTH;
					}

					// X step
					if( dx < 0 )
					{
						in_startx--;

						if( i == 0 )
						{
							// store pixels
							if( l_pen_index == 0 )
								g_gui_frame_buffer[address1] &= ~mask1;
							else
								g_gui_frame_buffer[address1] |= mask1;

							// new mask address
							i = 7;
							address1--;
							mask1 = 0;
						}
						else
							i--;
					}
					else
					{
						in_startx++;

						if( i == 7 )
						{
							// store pixels
							if( l_pen_index == 0 )
								g_gui_frame_buffer[address1] &= ~mask1;
							else
								g_gui_frame_buffer[address1] |= mask1;

							// new mask address
							i = 0;
							mask1 = 0;
							address1++;
						}
						else
							i++;
					}

					mask1 |= l_pixel_mask[i];

					// put the final pixel and leave the loop
					if( in_startx == in_endx )
					{
							if( l_pen_index == 0 )
								g_gui_frame_buffer[address1] &= ~mask1;
							else
								g_gui_frame_buffer[address1] |= mask1;

							break;
					}
				}
			}
		}
	}
}


///////////////////////////////////////////////////////////////////////////////
// Draw text
void guiDrawText( dosInt16 in_x, dosInt16 in_y, dosString in_string )
{
	dosROMAddress chardata;
	dosInt16 i;
	dosInt16 len;
	dosInt16 width;
	guiSize size;

	// check font
	if( g_gui_current_font.AsciiTableAddress == 0 )
		return;

	// check align
	if( g_gui_text_align != 0 )
	{
		// get text extent
		size = guiGetTextExtent( in_string );

		// horizontal align
		switch( g_gui_text_align & dosGUIHA_MASK )
		{
			// left align
			case dosGUIHA_LEFT:
				break;

			// center align
			case dosGUIHA_CENTER:
				in_x -= size.Width / 2;
				break;

			// right align
			case dosGUIHA_RIGHT:
				in_x -= size.Width;
				break;
		}

		// vertical align
		switch( g_gui_text_align & dosGUIVA_MASK )
		{
			// top align
			case dosGUIVA_TOP:
				break;

			// center align
			case dosGUIVA_CENTER:
				in_y -= size.Height / 2;
				break;

			// bottom align
			case dosGUIVA_BOTTOM:
				in_y -= size.Height;
				break;
		}
	}

	// draw characters
	len = strGetLength( in_string );
	for( i = 0; i < len; i++ )
	{
		if( in_string[i] >= g_gui_current_font.Minascii && in_string[i] <= g_gui_current_font.Maxascii )
		{
			chardata = dosROMReadWord( (in_string[i] - g_gui_current_font.Minascii) * sizeof( dosWord ) + g_gui_current_font.AsciiTableAddress );
			width = dosROMReadByte( chardata++ );

			guiBitBltFromROM(	chardata,
												in_x, in_y,
												0, 0, 
												width, g_gui_current_font.Height,
												1 );

			in_x += width;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw resource text
void guiDrawResourceText( dosInt16 in_x, dosInt16 in_y, dosHandle in_string_handle )
{
	dosROMAddress chardata;
	dosInt16 i;
	dosInt16 len;
	dosInt16 width;
  dosROMAddress string;
  dosChar ch;
	guiSize size;

  // check handle
  if( in_string_handle == krnlINVALID_HANDLE )
    return;

	// check font
	if( g_gui_current_font.AsciiTableAddress == 0 )
		return;

	// check align
	if( g_gui_text_align != 0 )
	{
		// get text extent
		size = guiGetResourceTextExtent( in_string_handle );

		// horizontal align
		switch( g_gui_text_align & dosGUIHA_MASK )
		{
			// left align
			case dosGUIHA_LEFT:
				break;

			// center align
			case dosGUIHA_CENTER:
				in_x -= size.Width / 2;
				break;

			// right align
			case dosGUIHA_RIGHT:
				in_x -= size.Width;
				break;
		}

		// vertical align
		switch( g_gui_text_align & dosGUIVA_MASK )
		{
			// top align
			case dosGUIVA_TOP:
				break;

			// center align
			case dosGUIVA_CENTER:
				in_y -= size.Height / 2;
				break;

			// bottom align
			case dosGUIVA_BOTTOM:
				in_y -= size.Height;
				break;
		}
	}

	// load string
  string = (dosInt16)krnlGetResourceString( in_string_handle );
	len = (dosInt16)krnlGetResourceStringLength( in_string_handle );

	// draw characters
	for( i = 0; i < len; i++ )
	{
    ch = dosROMReadByte( string + i );
		if( ch >= g_gui_current_font.Minascii && ch <= g_gui_current_font.Maxascii )
		{
			chardata = dosROMReadWord( (ch - g_gui_current_font.Minascii) * sizeof( dosWord ) + g_gui_current_font.AsciiTableAddress );
			width = dosROMReadByte( chardata++ );

			guiBitBltFromROM(	chardata,
												in_x, in_y,
												0, 0, 
												width, g_gui_current_font.Height,
												1 );

			in_x += width;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Draw bitmap
void guiDrawBitmap( dosInt16 in_dest_x, dosInt16 in_dest_y, dosHandle in_bitmap_handle )
{
	dosROMAddress resource_address;
	dosInt16 width;
	dosInt16 height;
	dosInt16 bpp;

	// get the bitmap address
	resource_address = /*l_bitmap_resource_address + */in_bitmap_handle;

	// get bitmap data
	width = dosROMReadWord( resource_address );
	resource_address += sizeof( dosWord );

	height = dosROMReadWord( resource_address );
	resource_address += sizeof( dosWord );

	bpp = dosROMReadByte( resource_address );
	resource_address += sizeof( dosByte );

	// do bitblt
	guiBitBltFromROM( resource_address,
										in_dest_x, in_dest_y,
										0, 0,
										width, height,
										bpp );
}

///////////////////////////////////////////////////////////////////////////////
// Bit-block-transfer for ROM bitmaps
void guiBitBltFromROM(	dosROMAddress in_address,
												dosInt16 in_dest_x, dosInt16 in_dest_y,
												dosInt16 in_source_x, dosInt16 in_source_y, 
												dosInt16 in_width, dosInt16 in_height,
												dosInt16 in_bits_per_pixel )
{
	dosInt16 i;
	dosInt16 source_scan_line_length;
	dosInt16 scan_line;
	dosInt16 destination_address;
	dosInt16 destination_row_end_address;
	dosInt16 destination_row_start_address;
	dosROMAddress source_row_start_address;
	dosROMAddress source_row_end_address;
	dosROMAddress source_address;
	dosByte source_start_mask;
	dosByte source_end_mask;
	dosByte destination_start_mask;
	dosByte destination_end_mask;
	dosInt8 shift;
	dosInt8 real_shift;
	dosWord data;
	dosByte invert_mask;
	dosBool transparent_mode;
	dosBool invert_mode;

	// init
	source_scan_line_length = (in_bits_per_pixel * in_width + 7 ) / 8;

	// do clipping
	if( in_dest_x > l_clip_rect.Right )
		return;

	if( in_dest_y > l_clip_rect.Bottom )
		return;

	if( in_dest_x + in_width < l_clip_rect.Left )
		return;

	if( in_dest_y + in_height < l_clip_rect.Top )
		return;

	// left side
	if( in_dest_x < l_clip_rect.Left )
	{
		i = l_clip_rect.Left - in_dest_x;

		in_dest_x += i; 
		in_source_x += i;
		in_width -=	i;

		if( in_width <= 0 )
			return;
	}

	// top side
	if( in_dest_y < l_clip_rect.Top )
	{
		i = l_clip_rect.Top - in_dest_y;

		in_dest_y += i; 
		in_source_y += i;
		in_height -=	i;

		if( in_height <= 0 )
			return;
	}

	// right side
	if( in_dest_x + in_width > l_clip_rect.Right )
	{
		in_width -= in_dest_x + in_width - l_clip_rect.Right - 1;

		if( in_width <= 0 )
			return;
	}

	// bottom side
	if( in_dest_y + in_height > l_clip_rect.Bottom )
	{
		in_height -= in_dest_y + in_height - l_clip_rect.Bottom - 1;

		if( in_height <= 0 )
			return;
	}

	// calculate destination addresses
	destination_row_start_address	=	destination_row_end_address = in_dest_y * guiFRAME_BUFFER_ROW_LENGTH;
	destination_row_start_address += in_dest_x / guiBITS_PER_PIXEL;
	destination_row_end_address += (in_dest_x + in_width-1) / guiBITS_PER_PIXEL;

	source_row_end_address = source_row_start_address = in_address + in_source_y * source_scan_line_length;
	source_row_start_address += in_source_x / 8;
	source_row_end_address += (in_source_x + in_width - 1) / 8;

	// get masks
	source_start_mask = l_pixel_row_start_mask[in_dest_x % guiBITS_PER_PIXEL];
	destination_start_mask = ~source_start_mask;
	source_end_mask = l_pixel_row_end_mask[(in_dest_x+in_width-1) % guiBITS_PER_PIXEL];
	destination_end_mask = ~source_end_mask;

	// calculate bit shift
	shift = (in_dest_x % guiBITS_PER_PIXEL) - (in_source_x % 8);

//	printf("s=%d srsa=%d ssm=%x sem=%x dsm=%x dem=%x",shift, source_row_start_address, source_start_mask, source_end_mask, destination_start_mask, destination_end_mask );

	// activate transparent mode
	if( (l_draw_mode & dosGUIDM_Transparent) != 0 )
	{
		transparent_mode = dosTrue;

		destination_start_mask	= 0xff;
		destination_end_mask		= 0xff;
	}
	else
		transparent_mode = dosFalse;

	// activate invert mode
	if( (l_draw_mode & dosGUIDM_Inverse) != 0 )
	{
		invert_mask = 0xff;
		invert_mode = dosTrue;
	}
	else
	{
		invert_mask = 0;
		invert_mode = dosFalse;
	}

	// do bitblt
	for( scan_line = 0; scan_line < in_height; scan_line++ )
	{
		source_address = source_row_start_address;
		destination_address = destination_row_start_address;

		if( destination_row_start_address == destination_row_end_address )
		{
			data =  ( dosROMReadByte( source_row_start_address ) << 8 ) +
							( dosROMReadByte( source_row_end_address ) );

			if( shift > 0 )
			{
				data >>= shift;
			}
			else
			{
				data <<= -shift;
				data = data >> 8;
			}

			if( transparent_mode )
			{
				if( invert_mode )
					g_gui_frame_buffer[destination_row_start_address] = (g_gui_frame_buffer[destination_row_start_address] & ( ~(data & source_start_mask & source_end_mask)));
				else
					g_gui_frame_buffer[destination_row_start_address] = (g_gui_frame_buffer[destination_row_start_address] | ( data & source_start_mask & source_end_mask));
			}
			else
				g_gui_frame_buffer[destination_row_start_address] = (g_gui_frame_buffer[destination_row_start_address] & (destination_start_mask | destination_end_mask)) | ( (data ^ invert_mask) & source_start_mask & source_end_mask);
		}
		else
		{
			// first byte
			if( shift > 0 )
			{
				data = ( dosROMReadByte( source_address++ ) ) << 8;
				data >>= shift;
				real_shift = 8 - shift;
			}
			else
			{
				data = (( dosROMReadByte( source_address++ )  ) << 8 );
				
				if( source_row_start_address != source_row_end_address )
					data |= dosROMReadByte( source_address++ );
				else
					data |= data >> 8;

				real_shift = -shift;

				data <<= real_shift;
			}

			if( invert_mode )
			{
				if( transparent_mode )
					g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address]) & ( ~((data >> 8 ) & source_start_mask) );
				else
					g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address] & destination_start_mask ) | ( (~(data >> 8 )) & source_start_mask );
			}
			else
				g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address] & destination_start_mask) | ( (data >> 8 ) & source_start_mask );
	
			destination_address++;

			// intermediate bytes
			while( destination_address < destination_row_end_address )
			{
				data <<= 8;

				data |= dosROMReadByte( source_address++ ) << real_shift;

				if( invert_mode )
				{
					if( transparent_mode )
						g_gui_frame_buffer[destination_address] = g_gui_frame_buffer[destination_address] | (~(data >> 8 ));
					else
						g_gui_frame_buffer[destination_address] = ~(data >> 8 );
				}
				else
					g_gui_frame_buffer[destination_address] = (dosByte)(data>>8);

				destination_address++;
			}

			// last byte
			data <<= 8;
			data |= ( dosROMReadByte( source_row_end_address ) ) << real_shift;

			if( invert_mode )
			{
				if( transparent_mode)
					g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address]) & ( ~((data >> 8 ) & source_end_mask) );
				else
					g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address] & destination_end_mask) | ((~(data >> 8 )) & source_end_mask );
			}
			else
				g_gui_frame_buffer[destination_address] = (g_gui_frame_buffer[destination_address] & destination_end_mask) | (( data >> 8 ) & source_end_mask );

			destination_address++;
		}

		destination_row_start_address += guiFRAME_BUFFER_ROW_LENGTH;
		destination_row_end_address += guiFRAME_BUFFER_ROW_LENGTH;
		source_row_start_address += source_scan_line_length;
		source_row_end_address += source_scan_line_length;
	}
}
